package moodish.comm.component;

import java.io.IOException;
import java.net.UnknownHostException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import moodish.comm.ClientComm;
import moodish.comm.ClientSideMessage;
import moodish.comm.ServerSideMessage;


/**
 * Communication interface for {@link moodish.client.MoodishClient Moodish clients}. Moodish clients must 
 * communicate with a MoodishServer exclusively through this interface. 
 * 
 * @author lmcro
 * @version 1.3
 */

public class ClientCommComponent implements ClientComm, Runnable{

	/**
	 * Establish a connection to a Moodish server. This method should be the first one called by 
	 * the client as no other operation (expect for {@link #isConnected()}) can be performed before
	 * a connection to the server has been established.
	 * 
	 * @param host                    Address of the server. For instance "localhost", "192.168.1.1", and so on.
	 * @param nickname                The nickname to use for the client.
	 * @throws UnknownHostException   Thrown if the host cannot be found.
	 * @throws IOException            Thrown if a connection cannot be established.
	 */
	
    private static final int serverPort = 8090;   // RFC6335] 8089-8090 unassigned 
	
	
	private Socket socketClientServer = null;
	private ObjectOutputStream outStream = null;
	private ObjectInputStream inStream = null;
	
	private String nickname = null;
	
	
	private BlockingQueue<ClientSideMessage> listOfMessages = new LinkedBlockingQueue<ClientSideMessage>();
	
	@Override
	public void connect(String host, String nickname) throws UnknownHostException, IOException
	{
		socketClientServer = new Socket(host, serverPort);
		
		outStream = new ObjectOutputStream(socketClientServer.getOutputStream());
		inStream = new ObjectInputStream(socketClientServer.getInputStream());
		
		
		listOfMessages = new LinkedBlockingQueue<ClientSideMessage>();
		this.nickname = nickname;
		
		outStream.writeObject(new ServerSideMessageComponent(nickname, null, ServerSideMessage.Type.CLIENT_CONNECTED));
		new Thread(this).start();
	}
	
	
	/**
	 * Check if currently connected to a host.
	 * 
	 * @return <b>true</b> if currently connected to a host, <b>false</b> otherwise.
	 */
	
	@Override
	public boolean isConnected() 
	 {
		
                System.out.println(socketClientServer);
                
                if (socketClientServer == null)
                 {        
                    
                    return false;
                  }     
                else
		 {
		    
                     return true;
		}          
                
         
	}

	
		
	/**
	 * Disconnect from a Moodish server. Any sockets should be closed immediately. 
	 * Pending messages are not guaranteed to be delivered before disconnecting.
	 */	
	@Override
	public void disconnect() {
		// It's only possible disconnection
		
		if(isConnected())
		  {
		   try 
		       {
				ClientSideMessageComponent message = new ClientSideMessageComponent(null,null,null);
				listOfMessages.put(message);
				socketClientServer.close();
				socketClientServer = null;
				
			   }
		   catch (IOException e)
		       {
				System.out.println("ERROR - Closing Socket !!!");
			   } 
		   catch (InterruptedException e)   //  thread is waiting, sleeping, or otherwise occupied
		       {
				System.out.println("ERROR - Message list !!!");
			   }
	  	    }
		
		else
		{
			System.out.println("You are Disconnected !!!");
		}
		
	}
		
	

	/**
	 * Get the next message received from the server. If no message 
	 * is has been received, the method blocks until a message has 
	 * been received. If no message will ever be received (connection is down), null is returned.
	 * 
	 * @return The next message sent by the server to the client or null if no message will ever be received (disconnected from server).
	 */
	@Override
	public ClientSideMessage getNextMessage()
	{
		
	// Next message is only possible if the user is connected and there are messages in the message list
    // There is a possibility that the list of messages is not empty but the system cannot access it
		
		ClientSideMessage message = null;
		if(isConnected() && listOfMessages != null)
		  {
			try 
			  {
				message = listOfMessages.take();
				
		      }
			 catch (InterruptedException e)  //  thread is waiting, sleeping, or otherwise occupied
			  {
				System.out.println("ERROR acessing the message list. Verify your conection !!!");
			  }
			
		}else
		   {
			System.out.println("You are Disconnected !!!");
		   }
		return message;
		
	}
	
	
	/** 
	 * Check if a message from the server is pending. If {@link #hasNextMessage()} returns <b>true</b>, a call to {@link #getNextMessage()} 
	 * will return immediately with the oldest message in the queue. If {@link #hasNextMessage()} returns <b>false</b>, a call to {@link #getNextMessage()} 
	 * will block until a message has been received.
	 * 
	 * @return <b>true</b> if a message from the server is currently pending, otherwise <b>false</b>.
	 */
	@Override
	public boolean hasNextMessage()
	{
//		if(listOfMessages.isEmpty())
//			return false;
//		return true;
		
//		return (listOfMessages != null);
//		return (listOfMessages.isEmpty());
		return  listOfMessages != null && !listOfMessages.isEmpty();
//		return true;
	 }	
	
	
	

	/**
	 * Send a Moodish message from this client to the server which should then relay the message to all freinds.
	 * 
	 * @param moodishMessage The Moodish message to be sent.
	 */
	@Override
	public void sendMoodishMessage(String moodishMessage)
	{
	// The message must only be sent if a user is connected
    		
	   if(isConnected()){
		   ServerSideMessageComponent message = new ServerSideMessageComponent(nickname, moodishMessage, ServerSideMessage.Type.MOODISH_MESSAGE);
		try
		  {
			outStream.writeObject(message);
		  }
		catch (IOException e) 
		  {
			System.out.println("Error communicating with Server !!!");
	   	}
	}
	    else{
		System.out.println("You are Disconnected !!!");
	}
	
	}
	
	
	/**
	 * Request to be friend of another client.
	 * 
	 * @param nickname the nickname of the client that should be friend.
	 */
	@Override
	public void friendship(String nickname)
	{
		if(isConnected())
		  {
			System.out.println("ClientCommComponent: Envio de Friendship para "+ nickname);
			ServerSideMessageComponent friendship = new ServerSideMessageComponent(nickname, this.nickname, ServerSideMessage.Type.FRIENDSHIP);
			
			try 
			{
			  outStream.writeObject(friendship);
			} 
			catch (IOException e) 
			{
			  System.out.println("Error communicating with Server !!!");
			}
	   	}
		else
		{
			System.out.println("You are Disconnected !!!");
		}
		
	}
	
	
	/**
	 * Request to unfriend of  a client.
	 * 
	 * @param nickname the nickname of the client currently friend that should be unfriended. 
	 */
	@Override
	public void unfriendship(String nickname)
	{
		if(isConnected())
		  {
			ServerSideMessageComponent unfriendship = new ServerSideMessageComponent(nickname, this.nickname, ServerSideMessage.Type.UNFRIENDSHIP);
			
			try 
			{
			  outStream.writeObject(unfriendship);
			} 
			catch (IOException e) 
			{
			  System.out.println("Error communicating with Server !!!");
			}
	   	}
		else
		{
			System.out.println("You are Disconnected !!!");
		}
		
	}
	
	

	@Override
	public void run() 
	{
		try 
		 {
		   while (isConnected()) 
		     { 
		    	try 
		    	 {
		    		listOfMessages.put((ClientSideMessageComponent) inStream.readObject());
				 } 
		    	catch (InterruptedException e)
		    	 {
					System.out.println("ERROR acessing the message list. Verify your conection !!!");
				 }
		     }
		} 
	        	catch (ClassNotFoundException e)
		         {
	        		e.printStackTrace();
		         }
		catch (IOException e)
		  {
			System.out.println("An error has occurred. Connection Terminated !!!");
		  }
		   
		
	}
	
	
	
	
	
	
	
	
	
	
	
}
